#include "workCmd.h"

static int recvn(int sockfd, void *buf, int length);

int workCmd(int netFd)
{
    client_t *client = (client_t*)calloc(1,sizeof(client_t));
    client->fd = netFd;

    //获取客户端名字
    int nameLength = 0;
    recvn(netFd,&nameLength,sizeof(int));
    recvn(netFd,client->userName,nameLength);

    //pwd保存基地址
    //client->pwd 保存客户端最后地址
    char pwd[1024];
    getcwd(pwd,sizeof(pwd));
    strcpy(client->pwd,pwd);
    //获取当前路径（基地址）
    printf("%s is connet ,pwd is %s fd = %d.\n",
           client->userName,pwd,client->fd);

    while(1){
        printf("%s$ \n",client->pwd);
        //接受命令
        TLVtrain_t tlv;
        recvn(netFd,&tlv.type,sizeof(tlv.type));
        recvn(netFd,&tlv.length,sizeof(tlv.length));
        recvn(netFd,tlv.data,tlv.length);
        printf("type = %d data = %s,cmdLength = %d\n",tlv.type,tlv.data,tlv.length);

        //分析命令
        if(tlv.type == CD){
            doCd(client);
        }
        else if(tlv.type == LS){
            doLs(client);
        }
        else if(tlv.type == LL){
            doLl(client);
        }
        //pwd客户端自己搞
        else if(tlv.type == PUTS){
            doPuts(client);
        }
        else if(tlv.type == GETS){
            doGets(client);
        }
        else if(tlv.type == REMOVE){
            doRemove(client);
        }
        else if(tlv.type == MKDIR){
            doMkdir(client);
        }
        else if(tlv.type == RMDIR){
            doRmdir(client);
        }
        else if(tlv.type == QUIT || tlv.length == 0){
            printf("%s has finished.\n",client->userName);
            free(client);
            break;
        }
    }
    return 0;
}

static int recvn(int sockfd, void *buf, int length){
    int total = 0;
    char *p = (char *)buf;
    while(total < length){
        ssize_t sret = recv(sockfd,p+total,length-total,0);
        total += sret;
    }
    return total;
}

//recvn封装版本，强制recv完，进度条
//小文件秒传，大文件慢慢传
int recvFile(int netfd){
    train_t train;
    char filename[4096] = {0};
    recvn(netfd,&train.length,sizeof(train.length));//收火车头
    recvn(netfd,train.data,train.length);//收车厢
    memcpy(filename,train.data,train.length);
    printf("%s\n",filename);

    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
    ERROR_CHECK(fd,-1,"open");

    off_t filesize = 0;
    recvn(netfd,&train.length,sizeof(train.length));
    recvn(netfd,train.data,train.length);
    memcpy(&filesize,train.data,train.length);

    printf("Filesize = %ld\n",filesize);
    if(filesize < 4096){
        printf("Tiny file.\n");
        ftruncate(fd,filesize);
        char *p = (char *)mmap(NULL,filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        recvn(netfd,p,filesize);
        munmap(p,filesize);
    }
    else{
        printf("Hurge file~\n");
        off_t currsize = 0;
        while(1){
            recvn(netfd,&train.length,sizeof(train.length));//收火车头
            if(train.length == 0){
                break;
            }
            currsize += train.length;
            printf("%5.2lf%%\r",100.0*currsize/filesize);
            fflush(stdout);

            sleep(1);
            recvn(netfd,train.data,train.length);//收车厢
            write(fd,train.data,train.length);
        }
    }
    printf("100.00%%\n");
    close(fd);
    return 0;
}

int sendFile(int netFd,char *file){
    train_t train;
    train.length = strlen(file);
    memcpy(train.data,file,train.length);
    printf("%d ,%s\n",train.length,train.data);
    send(netFd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(netFd,train.data,train.length,MSG_NOSIGNAL);

    int fd = open(file,O_RDWR);
    struct stat statbuf;
    fstat(fd,&statbuf);
    off_t filesize = statbuf.st_size;
    train.length = sizeof(filesize);
    memcpy(train.data,&filesize,train.length);
    send(netFd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(netFd,train.data,train.length,MSG_NOSIGNAL);

    printf("Filesize = %ld\n",filesize);
    //小文件秒传，大文件小火车传
    if(filesize < 4096){
        printf("Tiny file.\n");
        char *p = (char *)mmap(NULL,filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        send(netFd,p,filesize,MSG_NOSIGNAL);
        munmap(p,filesize);
    }
    else{
        printf("Hurge file~\n");
        while(1){
            bzero(train.data,sizeof(train.data));
            ssize_t ret = read(fd,train.data,sizeof(train.data));
            train.length = ret;
            send(netFd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
            send(netFd,train.data,train.length,MSG_NOSIGNAL);
            if(ret == 0){
                break;
            }
        }
    }
    close(fd);
    return 0;
}

int doCd(client_t *pclient){
    //接收目录名

    //拼接目录

    //验证路径

    //更新路径
    return 0;
}
int doLs(client_t *pclient){
    DIR *dir = opendir(pclient->pwd);
    ERROR_CHECK(dir,NULL,"opendir");

    train_t train;
    struct dirent *dirent_p;
    while((dirent_p = readdir(dir))!= NULL){
        bzero(&train,sizeof(train));
        train.length = strlen(dirent_p->d_name);
        strcpy(train.data,dirent_p->d_name);
        /* printf("%s ",train.data); */
        send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
        send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    }
    bzero(&train,sizeof(train));
    send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    closedir(dir);
    return 0;
}

static void trans_rwx(mode_t mode,char *mode_str);
static void trans_time(time_t mtime,char *time_str);
static int compare(const void *a,const void *b);

int doLl(client_t *pclient){
    DIR *dir = opendir(pclient->pwd);
    ERROR_CHECK(dir,NULL,"opendir");

    int ret_ch = chdir(pclient->pwd);
    ERROR_CHECK(ret_ch,-1,"chdir");

    /*先遍历一遍查看有多少个文件，再创建多少个结点*/
    int count = 0;
    struct dirent *dirent_p;
    while((dirent_p = readdir(dir))!= NULL){
        ++count;
    }
    struct dirent **dir_arr = (struct dirent**)malloc(sizeof(struct dirent*)*count);
    ERROR_CHECK(dir_arr,NULL,"dir_arr");
    rewinddir(dir);

    /*存入数组中*/
    int idex = 0;
    while((dirent_p = readdir(dir))!= NULL){
        dir_arr[idex] = dirent_p;
        ++idex;
    }
    /*排序*/
    qsort(dir_arr,count,sizeof(struct dirent*),compare);

    long total_size = 0;
    /*循环发送指针数组中文件信息*/
    train_t train;
    for(int i = 0;i < count; ++i){
        struct stat stat_buf;
        int ret = stat(dir_arr[i]->d_name,&stat_buf);
        total_size += stat_buf.st_blocks;
        ERROR_CHECK(ret,-1,"stat");

        char mode_str[11] = {0};
        trans_rwx(stat_buf.st_mode,mode_str);
        char time_str[100] = {0};
        trans_time(stat_buf.st_mtim.tv_sec,time_str);

        char info[4096] = {0};
        sprintf(info,"%s %2lu %s %s %5lu %13s %s",
                mode_str,
                stat_buf.st_nlink,
                getpwuid(stat_buf.st_uid)->pw_name,
                getgrgid(stat_buf.st_gid)->gr_name,
                stat_buf.st_size,
                time_str,
                dir_arr[i]->d_name);
        bzero(&train,sizeof(train));
        train.length = strlen(info);
        strcpy(train.data,info);
        /* printf("%s ",train.data); */
        send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
        send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    }
    char blok[256] = {0};
    sprintf(blok,"总用量 %lu",(total_size * 512)/1024);
    bzero(&train,sizeof(train));
    train.length = strlen(blok);
    strcpy(train.data,blok);
    send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);

    //发送退出信号
    bzero(&train,sizeof(train));
    send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    free(dir_arr);
    closedir(dir);
    return 0;
}

int doPuts(client_t *pclient){
    train_t train;
    char filename[4096];
    recvn(pclient->fd,&train.length,sizeof(train.length));
    recvn(pclient->fd,train.data,train.length);
    memcpy(&filename,train.data,train.length);
    printf("filename:%s\n",filename);

    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
    ERROR_CHECK(fd,-1,"open");
    
    off_t filesize = 0;
    recvn(pclient->fd,&train.length,sizeof(train.length));
    recvn(pclient->fd,train.data,train.length);
    memcpy(&filesize,train.data,train.length);
    printf("filesize:%ld\n",filesize);

    if(filesize > 4096){
        printf("huger file.\n");
        ftruncate(fd,filesize);
        char *p =(char *) mmap(NULL,filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        recvn(pclient->fd,p,filesize);
        munmap(p,filesize);
    }else{
        printf("tiny file\n");
        off_t currsize = 0;
        while(1){
            recvn(pclient->fd,&train.length,sizeof(train.length));
            if(train.length == 0){
                break;
            }
            currsize += train.length;
            printf("%5.21f%%\r",100.0*currsize/filesize);
            fflush(stdout);
            recvn(pclient->fd,train.data,train.length);
            write(fd,train.data,train.length);
        }
    }
    printf("100.00%%\n");
    close(fd);
    return 0;
}
int doGets(client_t *pclient){
    train_t train;
    train.length = strlen(file);
    memcpy(train.data,file,train.length);

    return 0;
}
int doRemove(client_t *pclient){
    return 0;
}
int doMkdir(client_t *pclient){
    return 0;
}
int doRmdir(client_t *pclient){
    return 0;
}


/* 转换权限 */
static void trans_rwx(mode_t mode,char *mode_str)
{
    /* 3*3+1+'\0' */
    //第一个位置是文件格式，先留着
    mode_str[1] = (mode & 0400)? 'r':'-';
    mode_str[2] = (mode & 0200)? 'w':'-';
    mode_str[3] = (mode & 0100)? 'x':'-';
    mode_str[4] = (mode & 0040)? 'r':'-';
    mode_str[5] = (mode & 0020)? 'w':'-';
    mode_str[6] = (mode & 0010)? 'x':'-';
    mode_str[7] = (mode & 0004)? 'r':'-';
    mode_str[8] = (mode & 0002)? 'w':'-';
    mode_str[9] = (mode & 0001)? 'x':'-';

    mode_str[10] = '\0';

    /* 查看inode */
    switch(mode & S_IFMT){
    case S_IFBLK: mode_str[0] = 'b';  break;
    case S_IFCHR: mode_str[0] = 'c';  break;
    case S_IFSOCK: mode_str[0] = 's';  break;
    case S_IFLNK: mode_str[0] = 'l';  break;
    case S_IFDIR: mode_str[0] = 'd';  break;
    case S_IFIFO: mode_str[0] = 'p';  break;
    case S_IFREG: mode_str[0] = '-';  break;
    default:mode_str[0] = '?';break;
    }
}

/* 转换时间 */
static void trans_time(time_t mtime,char *time_str)
{
    struct tm* st_tm = localtime(&mtime);
    sprintf(time_str,"%2d月 %2d %02d:%02d",
            st_tm->tm_mon+1,
            st_tm->tm_mday,
            st_tm->tm_hour,
            st_tm->tm_min);
}

static int compare(const void *a,const void *b)
{
    /*按照字典顺序排序*/
    struct dirent * dir_a = *(struct dirent **)a;
    ERROR_CHECK(dir_a,NULL,"dir_a");
    struct dirent * dir_b = *(struct dirent **)b;
    ERROR_CHECK(dir_b,NULL,"dir_b");
    /* puts("1\n"); */
    return strcasecmp(dir_a->d_name,dir_b->d_name);
}


